package com.xcity.game.combat.listener.combat;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;

import com.xcity.Server;
import com.xcity.db.entity.Build;
import com.xcity.db.entity.Build.State;
import com.xcity.db.entity.Employee;
import com.xcity.db.entity.Player;
import com.xcity.game.build.BuildCaptureCallback;
import com.xcity.game.build.BuildService;
import com.xcity.game.build.BuildUtils;
import com.xcity.game.combat.Combat;
import com.xcity.game.combat.Combat.Type;
import com.xcity.game.combat.CombatException;
import com.xcity.game.combat.CombatService;
import com.xcity.game.combat.Round;
import com.xcity.game.combat.builder.PVPCombatBuilder;
import com.xcity.game.combat.listener.CombatListener;
import com.xcity.game.common.Cause;
import com.xcity.game.common.GameStaticConfig;
import com.xcity.game.common.MessageConst;
import com.xcity.game.event.GameEvents;
import com.xcity.game.formation.Formation;
import com.xcity.game.news.NewsService;
import com.xcity.game.news.NewsType;
import com.xcity.game.player.cache.PlayerCacheService;
import com.xcity.pomelo.push.NoticePush;
import com.xcity.pomelo.push.msg.BeAttackedPush;
import com.xcity.pomelo.push.msg.CombatEndPush;
import com.xcity.pomelo.request.CombatInfoRequest;
import com.xcity.pomelo.request.CombatPVPRequest;
import com.xcity.pomelo.response.CombatInfoResponse;
import com.xcity.pomelo.response.CombatInfoResponse.Team;
import com.xcity.pomelo.response.CombatInfoResponse.TeamBuilder;
import com.xcity.pomelo.response.CombatResponse;

import naga.x.App;
import naga.x.game.time.TimeUpdater;
import naga.x.net.Packet;
import naga.x.net.Session;

@Deprecated
public class PVPCombatListener implements CombatListener, BuildCaptureCallback {

	private Action action;
	private Player source, target;
	private Build build;
	private Result result;
	protected static final Logger LOG = CombatService.LOG;
	
	static Map<Long, BuildCaptureCallback> callbacks = new ConcurrentHashMap<>(16);
	private long timeout;
	
	static enum Action {
		INVADE,    // 掠夺别人未被占领的建筑（侵攻战）
		BEAT_BACK, // 夺回被别人占领的建筑（反击战）
		BLOCK      // 掠夺被别人占领的别人的建筑（阻击战）
	}
	
	@Override
	public void prepareCombat(Session session, CombatInfoRequest packet) throws CombatException {
		long targetPlayerId = packet.getPVPTargetPlayerId();
		int targetBuildId = packet.getPVPTargetBuildId();
		validate(session, targetPlayerId, targetBuildId);
		
		TeamBuilder sourceTeamBuilder = new TeamBuilder().setLeader(source);//.setCombatPower(CombatUtils.calcCombatPowerWithEmployees(source));
		long[] srcEmployeeIds = source.getFormations().get(Formation.TYPE_NORMAL).getIds();//CombatUtils.getCombatFormation(source, true);
		for (long eid : srcEmployeeIds) {
			sourceTeamBuilder.addEmployeeMember(eid);
		}
		
		Team sourceTeam = sourceTeamBuilder.build();
		
		long[] eids = build.employees();//build.getEmployees();
		TeamBuilder targetTeamBuilder = new TeamBuilder().setLeader(target);//.setCombatPower(CombatUtils.calcCombatPower(target, eids));
		if (eids == null) eids = Formation.EMPTY_FORMATION;
		for (long eid : eids) {
			targetTeamBuilder.addEmployeeMember(eid);
		}
		Team targetTeam = targetTeamBuilder.build();
		
		session.send(new CombatInfoResponse(packet.getSerial(), sourceTeam, targetTeam, Formation.TYPE_NORMAL));
	}
	
	private void validate(Session session, long targetPlayerId, int targetBuildId) throws CombatException {
		if (targetPlayerId == 0L || targetBuildId == 0) {
			throw new CombatException(MessageConst.ILLEGAL_OPERATION);
		}
		Player player = session.getClient();
//		long[] srcEmployeeIds = CombatUtils.getFormation(player, true);
		Player target = App.getApp().getServiceManager().get(PlayerCacheService.class).find(targetPlayerId);
		if (target == null) {
			throw new CombatException(MessageConst.PLAYER_NOT_EXIST);
		}
		Build build = target.getBuildings().getBuild(targetBuildId);
		if (build == null) {
			throw new CombatException(MessageConst.TARGET_PLAYER_BUILD_NOT_EXIST);
		}
		build.update(); // 强制更新一下
		if (build.getState() == State.BUILDING) {
			throw new CombatException(MessageConst.BUILD_UPGRADING);
		}
		if (build.getState() == State.REST) {
			throw new CombatException(MessageConst.BUILD_REST);
		}
		if (build.getLevel() <= Build.BREAK_LEVEL) {
			throw new CombatException(MessageFormat.format(MessageConst.BUILD_ATTACK_LVL_LIMIT, Build.BREAK_LEVEL));
		}
		int gold = build.settle(); // 先计算收入
		if (gold <= 0) {
			throw new CombatException(MessageConst.BUILD_NO_INCOME);
		}
		Player buildOwnerNow = build.owner();
		if (player.getId() == target.getId() && target.getId() == buildOwnerNow.getId()) {
			throw new CombatException(MessageConst.COMBAT_PVP_CAN_NOT_ATTACK_SELF);
		}
		if (player.getId() == buildOwnerNow.getId()) {
			throw new CombatException(MessageConst.BUILD_OCCUPIED);
		}
		this.source = player;
		this.target = buildOwnerNow;
		this.build = build;
		if (player.getId() != target.getId()) {
			if (build.getAttackerId() == 0L) {
				action = Action.INVADE;
			} else {
				action = Action.BLOCK;
			}
		} else {
			action = Action.BEAT_BACK;
		}
		if (action != Action.BEAT_BACK && player.getConquests().isFull()) {
			throw new CombatException(MessageFormat.format(MessageConst.BUILD_OCCUPIED_EXCEED, GameStaticConfig.captureBuildMaxCount));
		}
	}
	
	@Override
	public CombatResponse create(CombatService service, Type type, Session session, Packet packet) throws CombatException {
		CombatPVPRequest req = (CombatPVPRequest) packet;
		validate(session, req.getTargetId(), req.getTargetBuildId());
		// 检测体力
//		int decStr = (int) (action == Action.BEAT_BACK ? PlayerUtils.getMaxStrength(source) * GameStaticConfig.beatBackDecStrRatio : BuildService.ATTACK_DEC_STR);
//		if (decStr < 1) {
//			decStr = 1;
//		}
		int decStr = 0;
		if (source.getStrength() < decStr) {
			throw new CombatException(MessageConst.STR_NOT_ENOUGH);
		}
		long[] srcEmployeeIds = source.getFormations().get(Formation.TYPE_NORMAL).getIds();//CombatUtils.getCombatFormation(source);
		if (srcEmployeeIds == null || srcEmployeeIds.length == 0) {
			throw new CombatException(MessageConst.FORMATION_EMPTY);
		}
		
		List<Employee> srcEmployees = new ArrayList<Employee>(srcEmployeeIds.length);
		// 遍历检查一遍行动力,hp,state
		int srcEmployeesSize = 0;
		for (long eid : srcEmployeeIds) {
			Employee e;
			if (eid == 0L || (e = source.getEmployees().get(eid)) == null) {
				srcEmployees.add(null);
				continue;
			}
			com.xcity.db.entity.Employee.State state = com.xcity.db.entity.Employee.State.IN_FORMATION;
			if (state.enable && e.getState() != state) {
				throw new CombatException(MessageConst.EMPLOYEE_COMBAT_STATE_ERROR);
			}
			if (e.getEnergy() < 1) {
				throw new CombatException(MessageFormat.format(MessageConst.EMPLOYEE_ENERGY_NOT_ENOUGH, e.getName()));
			}
			srcEmployees.add(e);
			++srcEmployeesSize;
		}
		long[] tgtEmployeeIds = build.employees();
		List<Employee> tgtEmployees = new ArrayList<Employee>(tgtEmployeeIds != null ? tgtEmployeeIds.length : Formation.FORMATION_CAPCITY);
		int tgtEmployeesSize = 0;
		if (tgtEmployeeIds != null) {
			for (long eid : tgtEmployeeIds) {
				Employee e = eid != 0L ? target.getEmployees().get(eid) : null;
				tgtEmployees.add(e);
				if (e != null) {
					++tgtEmployeesSize;
				}
			}
		}
//		if (srcEmployeesSize > tgtEmployeesSize) {
//			String msg = tgtEmployeesSize > 0 ?
//					MessageFormat.format(MessageConst.COMBAT_EMPLOYEES_EXCEED, tgtEmployeesSize)
//					: MessageConst.COMBAT_EMPLOYEE_CAN_NOT_IN;
//			throw new CombatException(msg);
//		}
		
		
		// 攻击方数值计算;扣除体力、行动力
		source.decStrength(decStr, Cause.BUILD_ATTACK);
//		for (int i = 0; i < srcEmployees.size(); i++) {
//			Employee e = srcEmployees.get(i);
//			// 绝处逢生技能计算(扣行动力)
////			if (RND.nextInt(GameStaticConfig.percentBase) >= attacker.getSkillValue(SkillTypes.NOT_DEC_ENERGY_ATTACK_BUILD, 0)) {
//			if (e != null) {
//				e.decEnergy(1, Cause.BUILD_ATTACK);
//			}
////			}
//		}
		Combat combat = new PVPCombatBuilder()
				.setType(Combat.Type.PVP)
				.setSource(source).setSourceEmployees(srcEmployees)
				.setTarget(target).setTargetEmployees(tgtEmployees)
				.setListener(this)
				.build();
		LOG.info("[PVP]COMBAT[{}]SOURCE[{}]TARGET[{}]BUILD[{}]OWNER[{}]", combat.getId(), source.getId(), target.getId(), build.getId(), build.getManager().getOwner().getId());
		return new CombatResponse(req.getSerial(), combat);
	}

	@Override
	public void end(Combat combat, boolean notify) {
		boolean succ = combat.isWinner(source.getId());
		int gold = 0;
		if (succ) {
			forceRefresh(); // 重新取一遍build数据，避免目标玩家重新上线导致缓存不一致
			gold = build.settle();
			build.resetIncome();
			build.rest(source, build.getCure() + BuildUtils.getEQFactorOfEmployees(build));
			Player buildOwner = build.getManager().getOwner(); // 建筑原主人
			if (buildOwner.getId() != source.getId() && TimeUpdater.getInstance().now() - BuildService.lastBroadcastBuildAttackTime > BuildService.BROADCAST_BUILD_ATTACK_CD) {
				String notice = MessageFormat.format(MessageConst.BUILD_ATTACK_BROADCAST, buildOwner.getName(), build.getName(), source.getName());
				Server.broadcast(new NoticePush(NoticePush.NOTICE_MARQUEE, notice));
				BuildService.lastBroadcastBuildAttackTime = TimeUpdater.getInstance().now();
			}
//			int restCD = build.getLeftRestCD();
//			Date _restCD = new Date();
//			_restCD.setTime(restCD);
//			App.getApp().getServiceManager().get(MailService.class).sendSystemMail(target.getId(),
//					MessageConst.BUILD_ATTACK_MAIL_TITLE,
//					MessageFormat.format(MessageConst.BUILD_ATTACK_MAIL_CONTENT, build.getName(), new Date(), source.getName(), source.getId(), gold, _restCD));
			source.addGold(gold, Cause.BUILD_ATTACK);
			
//			if (buildOwner instanceof Actor) {
//				((Actor) buildOwner).setDirty(true);
//			}
			if (buildOwner.getId() != source.getId() && buildOwner.isOnline()) {
//				buildOwner.send(new CustomSync(SyncPush.SYNC_BUILD, new BuildStatusRes(buildOwner, build)));
				buildOwner.send(new BeAttackedPush(build.getId()));
			}
		}
		byte b = succ ? Combat.SUCC : Combat.FAIL;
		if (notify) {
			source.send(new CombatEndPush(b, Combat.Type.PVP, combat.getId(), result = new Result(build, gold, succ)));
		}
		App.getApp().getEventManager().addEvent(GameEvents.EVENT_ATTACK_BUILD, source, build.getManager().getOwner(), build, gold);
		
		if (succ && action != Action.BEAT_BACK) { // 胜利且非反击战 需要等玩家操作后才能确定邮件内容
			timeout = TimeUpdater.getInstance().now() + Build.CAPTURE_OPERATE_TIMEOUT + 5000;
			callbacks.put(source.getId(), this);
		} else {
			onMsg(succ, false);
		}
		
		LOG.info("[PVP END]COMBAT[{}]SOURCE[{}]TARGET[{}]BUILD[{}]OWNER[{}]RESULT[{}]", combat.getId(), source.getId(), target.getId(), build.getId(), build.getManager().getOwner().getId(), b);
	}
	
	private void forceRefresh() {
		Player player = build.getManager().getOwner();
		player = App.getApp().getServiceManager().get(PlayerCacheService.class).find(player.getId());
		build = player.getBuildings().getBuild(build.getId());
	}
	
	private void onMsg(boolean succ, boolean captured) {
		switch (action) {
			case INVADE:
				onMsgInvade(succ, captured);
				break;
			case BEAT_BACK:
				onMsgBeatBack(succ);
				break;
			case BLOCK:
				onMsgBlock(succ, captured);
				break;
		}
	}
	
	// 攻打别人
	private void onMsgInvade(boolean succ, boolean captured) {
		// to source
//		攻击别人自己赢了：您于2016年11月7日17点15分侵占了 大傻子 的大自然氧吧，虽然 大傻子 带着员工奋力反抗，但并没什么卵用，您还是获得了112500 现金以及店面拥有权
//		攻击别人自己输了：您于2016年11月7日17点15分侵占了 大傻子 的大自然氧吧，大傻子 带着员工奋力反抗，您未能如愿以偿。
		Date now = new Date(TimeUpdater.getInstance().now());
		String title = MessageConst.BUILD_ATTACK_MAIL_TITLE;
		String msg = succ ?
				MessageFormat.format(captured ? MessageConst.COMBAT_PVP_ATTACK_SUCC2 : MessageConst.COMBAT_PVP_ATTACK_SUCC, now, target.getName(), target.getId(), build.getName(), result.gold)
				: MessageFormat.format(MessageConst.COMBAT_PVP_ATTACK_FAIL, now, target.getName(), target.getId(), build.getName());
		NewsService newsService = App.getApp().getServiceManager().get(NewsService.class);
		newsService.send(source, NewsType.PVP_RESULT, title, msg);
		// to target
//		被攻击自己输了：您的 五谷煎饼店 于2016年11月7日17点15分 被 大傻子 非法侵占，虽然您和您的员工奋力反抗，但并没什么卵用，您损失了 125000 现金以及店面的拥有权 
//		被攻击自己赢了：您的 五谷煎饼店 于2016年11月7日17点15分 被 大傻子 非法侵占，经过您和您的员工奋力反抗 大傻子 未能如愿以偿 
		msg = succ ? 
				MessageFormat.format(captured ? MessageConst.COMBAT_PVP_DEFENSE_FAIL2 : MessageConst.COMBAT_PVP_DEFENSE_FAIL, build.getName(), now, source.getName(), source.getId(), result.gold)
				: MessageFormat.format(MessageConst.COMBAT_PVP_DEFENSE_SUCC, build.getName(), now, source.getName(), source.getId());
		newsService.send(target, NewsType.PVP_RESULT, title, msg);
	}
	
	// 夺回被别人占领建筑
	private void onMsgBeatBack(boolean succ) {
//		被人干了反击还失败：您于 2016年11月7日17点15分在被 大傻子 侵占的 五谷煎饼店 奋力反抗 大傻子 的非法行为，虽然没什么卵用，但极大的打击了他们的嚣张气焰
//		被人干了反击成功了：您于 2016年11月7日17点15分 在被 大傻子 侵占的 五谷煎饼店 前维权成功，夺回了原本属于您的财产
		Date now = new Date(TimeUpdater.getInstance().now());
		String title = MessageConst.BUILD_ATTACK_MAIL_TITLE;
		String msg = succ ?
				MessageFormat.format(MessageConst.COMBAT_PVP_BEAT_BACK_SUCC, now, target.getName(), target.getId(), build.getName())
				: MessageFormat.format(MessageConst.COMBAT_PVP_BEAT_BACK_FAIL, now, target.getName(), target.getId(), build.getName());
		NewsService newsService = App.getApp().getServiceManager().get(NewsService.class);
		newsService.send(source, NewsType.PVP_RESULT, title, msg);
		
		if (succ) { // 通知建筑原占有者
			msg = MessageFormat.format(MessageConst.COMBAT_PVP_BEAT_BACK_SUCC_FOR_TARGET, source.getName(), source.getId(), build.getName());
			newsService.send(target, NewsType.PVP_RESULT, title, msg);
		}
	}
	
	// 攻打别人被占领建筑
	private void onMsgBlock(boolean succ, boolean captured) {
//		别人在自己家干起来老东家赢了：二愣子 在 大傻子 非法侵占的您的 五谷煎饼店 中对 大傻子 大打出手，结果反被 大傻子 打的头破血流，撒丫子跑了。
//		别人在自己家干起来老东家输了：二愣子 在 大傻子 非法侵占的您的 五谷煎饼店 中对 大傻子 大打出手，结果 大傻子 被打蒙圈了，放弃了店面逃跑了。
		Player player = build.getManager().getOwner(); // 该建筑的原主人
		String msg = null;
		if (!succ) {
			msg = MessageFormat.format(MessageConst.COMBAT_PVP_BLOCK_FAIL, source.getName(), source.getId(), target.getName(), target.getId(), build.getName());
		} else if (captured) {
			msg = MessageFormat.format(MessageConst.COMBAT_PVP_BLOCK_SUCC, source.getName(), source.getId(), target.getName(), target.getId(), build.getName());
		}
		if (msg == null) {
			return;
		}
		String title = MessageConst.BUILD_ATTACK_MAIL_TITLE;
		NewsService newsService = App.getApp().getServiceManager().get(NewsService.class);
		newsService.send(target, NewsType.PVP_RESULT, title, msg);
		
		if (succ) { // 通知建筑原占有者
			msg = MessageFormat.format(MessageConst.COMBAT_PVP_BLOCK_SUCC_FOR_TARGET, player.getName(), player.getId(), build.getName(), source.getName(), source.getId());
			newsService.send(target, NewsType.PVP_RESULT, title, msg);
		}
	}
	
	static class Result {
		public int gold; // 获得现金
		public long pid; // 目标玩家id
		public int bid; // 目标建筑id
		public byte status;
		public int restCD;
		private boolean succ;
		public Result(Build build, int gold, boolean succ) {
			this.gold = gold;
			this.pid = build.getManager().getOwner().getId();
			this.bid = build.getId();
			this.status = build.getState().codeOfOldVersion();
			this.restCD = build.getLeftRestCD();
			this.succ = succ;
		}
	}

	@Override
	public void captured() {
		onMsg(result.succ, true);
	}

	@Override
	public void unCaptured() {
		onMsg(result.succ, false);
	}
	
	@Override
	public boolean isTimeout() {
		return timeout < TimeUpdater.getInstance().now();
	}
	
	public static void doCaptureCallback(Player source, boolean captured) {
		BuildCaptureCallback callback = callbacks.remove(source.getId());
		if (callback != null) {
			if (captured) {
				callback.captured();
			} else {
				callback.unCaptured();
			}
		}
	}
	
	public static void update() {
		if (callbacks.isEmpty()) {
			return;
		}
		Iterator<Entry<Long, BuildCaptureCallback>> itr = callbacks.entrySet().iterator();
		while (itr.hasNext()) {
			Entry<Long, BuildCaptureCallback> e = itr.next();
			BuildCaptureCallback callback = e.getValue();
			if (callback.isTimeout()) {
				itr.remove();
				callback.unCaptured();
			}
		}
	}

	@Override
	public void roundStart(Round round) {
	}

	@Override
	public void roundEnd(Round round) {
	}

}
